//
// Tlv.cs
//
// Copyright (c) 2007 Lukas Lipka.
//

using System;
using System.Collections.Generic;
using System.Text;

namespace IcqSharp.Util
{
    public class Tlv
    {
        private UInt16 type;
        private byte[] data;

        public Tlv (UInt16 type, byte[] data)            
        {
            this.type = type;
            this.data = data;
        }

        public Tlv (UInt16 type, string str)
            : this (type, Encoding.UTF8.GetBytes (str))
        {
        }

        public Tlv(UInt16 type, Int32 value)
        {
            this.type = type;
            this.data = EndianBitConverter.Big.GetBytes (value);
        }

        public Tlv(UInt16 type, UInt32 value)
        {
            this.type = type;
            this.data = EndianBitConverter.Big.GetBytes(value);
        }

        public Tlv (UInt16 type, Int16 value)
        {
            this.type = type;
            this.data = EndianBitConverter.Big.GetBytes (value);
        }

        public Tlv(UInt16 type, UInt16 value)
        {
            this.type = type;
            this.data = EndianBitConverter.Big.GetBytes(value);
        }

        public Tlv(UInt16 type)
        {
            this.type = type;
            this.data = null;
        }

        public void Add (byte[] bytes)
        {
            if (data != null) {
                byte[] buffer = new byte[data.Length + bytes.Length];
                Array.Copy (data, buffer, data.Length);
                Array.Copy (bytes, 0, buffer, data.Length, bytes.Length);
                data = buffer;
            } else {
                data = bytes;
            }
        }

        public override string ToString ()
        {
            return Encoding.UTF8.GetString (data, 0, data.Length);
        }

        public UInt32 ToUInt32 ()
        {
            return EndianBitConverter.Big.ToUInt32 (data, 0);
        }

        public Int32 ToInt32 ()
        {
            return EndianBitConverter.Big.ToInt32 (data, 0);
        }

        public Int16 ToInt16 ()
        {
            return EndianBitConverter.Big.ToInt16 (data, 0);
        }

        public UInt16 ToUInt16 ()
        {
            return EndianBitConverter.Big.ToUInt16 (data, 0);
        }
        
        public UInt16 Type
        {
            get { return type; }
        }

        public UInt16 Length
        {
            get { return (ushort)(data == null ? 0 : data.Length); }
        }

        public byte[] Data
        {
            get { return data; }
        }

        public byte[] GetBytes ()
        {
            int buffer_length = 4;

            if (data != null)
                buffer_length += data.Length;

            byte[] buffer = new byte[buffer_length];
            byte[] bytes = null;

            // TLV type
            bytes = EndianBitConverter.Big.GetBytes (type);
            Array.Copy (bytes, 0, buffer, 0, 2);

            // TLV length
            bytes = EndianBitConverter.Big.GetBytes ((ushort)(buffer_length - 4));
            Array.Copy (bytes, 0, buffer, 2, 2);

            // TLV data
            if (data != null)
                Array.Copy (data, 0, buffer, 4, data.Length);

            return buffer;
        }

        public static Tlv Parse (byte[] data, int index)
        {
                ushort tlv_type = LittleEndianBitConverter.Big.ToUInt16 (data, index);
                ushort tlv_length = LittleEndianBitConverter.Big.ToUInt16(data, index + 2);

                byte[] tlv_data = new byte[tlv_length];
                Array.Copy(data, index + 4, tlv_data, 0, tlv_length);

                return new Tlv(tlv_type, tlv_data);
        }

        public static Tlv Parse (byte[] list)
        {
            return Parse (list, 0);
        }
    }
}